In [1]:
import numpy as np
import pandas as pd

# sklearn
from sklearn.metrics import confusion_matrix
from sklearn.model_selection import train_test_split
from sklearn.datasets import make_moons, make_circles, make_classification
from sklearn import metrics
from sklearn.cluster import KMeans
from sklearn.datasets import load_digits
from sklearn.decomposition import PCA
from sklearn.preprocessing import scale

# Tensorflow
import tensorflow as tf

# Keras
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten, Conv2D, MaxPooling2D
from keras.optimizers import SGD, Adagrad
from keras.utils.vis_utils import plot_model
from keras.utils import to_categorical
# Visualisation libraries

## matplotlib
import matplotlib.pyplot as plt
%matplotlib inline

## seaborn
import seaborn as sns
sns.set_context("paper", rc={"font.size":12,"axes.titlesize":14,"axes.labelsize":12})
sns.set_style("whitegrid")

## plotly
from plotly.offline import init_notebook_mode, iplot 
import plotly.graph_objs as go
import plotly.offline as py
from plotly.subplots import make_subplots
import plotly.express as px
%config InlineBackend.figure_format = 'retina' 

import warnings
warnings.filterwarnings("ignore")
Using TensorFlow backend.
Basic Image Classifications with Keras

In this article, we go through basic image classification using Multi-layer Perceptron (MLP). For testing the algorithm, we use sklearn digit dataset.

In [2]:
fig, ax = plt.subplots(1, 10, figsize=(17, 6))
ax = ax.ravel()
digits = load_digits()
for ax, (image, label) in zip(ax, list(zip(digits.images, digits.target))):
    ax.set_axis_off()
    ax.imshow(image, cmap= 'Greys', interpolation='nearest')
    ax.set_title('%i' % label, fontsize = 10)
X = digits.images
X = X / 256.0
y = digits.target 
Labels = np.unique(y)

Train and Test sets

One of the efficient methods of splitting a dataset into random train and test subsets is using sklearn.model_selection.train_test_split.

In [3]:
y = to_categorical(y, num_classes=len(Labels))

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

pd.DataFrame(data={'Set':['X_train','X_test','y_train','y_test'],
               'Shape':[X_train.shape, X_test.shape, y_train.shape, y_test.shape]}).set_index('Set').T
Out[3]:
Set X_train X_test y_train y_test
Shape (1257, 8, 8) (540, 8, 8) (1257, 10) (540, 10)

Image Classification

The goal of this approach is to classify the images by focusing on the relationship between nearby pixels. A simple implementation of an image classifier can be performed in Keras using Multi-layer Perceptron (MLP) Image Classification as follows.

In [4]:
model = Sequential(name = 'Multi-layer Perceptron (MLP) Image Classification')
model.add(Flatten(input_shape=X[0].shape, name='Layer1'))
model.add(Dropout(0.5))
model.add(Dense(32, activation='relu', name='Layer2'))
model.add(Dropout(0.5))
model.add(Dense(len(Labels), activation='softmax', name='Layer3'))
#
model.summary()
plot_model(model, show_shapes=True, show_layer_names=True, expand_nested = True)
Model: "Multi-layer Perceptron (MLP) Image Classification"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
Layer1 (Flatten)             (None, 64)                0         
_________________________________________________________________
dropout_1 (Dropout)          (None, 64)                0         
_________________________________________________________________
Layer2 (Dense)               (None, 32)                2080      
_________________________________________________________________
dropout_2 (Dropout)          (None, 32)                0         
_________________________________________________________________
Layer3 (Dense)               (None, 10)                330       
=================================================================
Total params: 2,410
Trainable params: 2,410
Non-trainable params: 0
_________________________________________________________________
Out[4]:
In [5]:
# Number of iterations
IT = int(5e2)+1

model.compile(optimizer= 'rmsprop', loss='binary_crossentropy', metrics=['accuracy','mae', 'mse'])

# Training model
history = model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs= IT, verbose = 0)
In [6]:
def Search_List(Key, List): return [s for s in List if Key in s]

Metrics_Names = {'loss':'Loss', 'accuracy':'Accuracy', 'mae':'MAE', 'mse':'MSE'}

def Table_modify(df, Metrics_Names = Metrics_Names):
    df = df.rename(columns = Metrics_Names)
    df = df.reindex(sorted(df.columns), axis=1)
    df.insert(loc = 0, column = 'Iteration', value = np.arange(0, df.shape[0]), allow_duplicates=False)
    return df

Validation_Table = Search_List('val_',history.history.keys()) 
Train_Table = list(set( history.history.keys()) - set(Validation_Table))
Validation_Table = pd.DataFrame(np.array([history.history[x] for x in Validation_Table]).T, columns = Validation_Table)
Train_Table = pd.DataFrame(np.array([history.history[x] for x in Train_Table]).T, columns = Train_Table)
Validation_Table.columns = [x.replace('val_','') for x in Validation_Table.columns]

Train_Table = Table_modify(Train_Table)
Validation_Table = Table_modify(Validation_Table)

# Train Set Score
score = model.evaluate(X_test, y_test, batch_size=128, verbose = 0) 
score = pd.DataFrame(score, index = model.metrics_names).T
score.index = ['Train Set Score']
# Validation Set Score
Temp = model.evaluate(X_train, y_train, batch_size=128, verbose = 0) 
Temp = pd.DataFrame(Temp, index = model.metrics_names).T
Temp.index = ['Validation Set Score']
score = score.append(Temp)
score.rename(columns= Metrics_Names, inplace = True)
score = score.reindex(sorted(score.columns), axis=1)
display(score.style.set_precision(4))
Accuracy Loss MAE MSE
Train Set Score 0.9793 0.0723 0.0558 0.0185
Validation Set Score 0.9800 0.0723 0.0558 0.0180

Let's define some function by which we can analyze the performance of the modeling.

In [7]:
def Plot_history(history, yLim = 2, Title = False, Table_Rows = 25):
    fig = make_subplots(rows=1, cols=2, horizontal_spacing = 0.02, column_widths=[0.6, 0.4],
                        specs=[[{"type": "scatter"},{"type": "table"}]])
    # Left
    fig.add_trace(go.Scatter(x= history['Iteration'].values, y= history['Loss'].values,
                             line=dict(color='OrangeRed', width= 1.5), name = 'Loss'), 1, 1)
    fig.add_trace(go.Scatter(x= history['Iteration'].values, y= history['Accuracy'].values,
                             line=dict(color='MidnightBlue', width= 1.5),  name = 'Accuracy'), 1, 1)
    fig.add_trace(go.Scatter(x= history['Iteration'].values, y= history['MAE'].values,
                             line=dict(color='ForestGreen', width= 1.5), name = 'Mean Absolute Error (MAE)'), 1, 1)
    fig.add_trace(go.Scatter(x= history['Iteration'].values, y= history['MSE'].values,
                             line=dict(color='purple', width= 1.5), name = 'Mean Squared Error (MSE)'), 1, 1)
    fig.update_layout(legend=dict(x=0, y=1.1, traceorder='reversed', font_size=12),
                  dragmode='select', plot_bgcolor= 'white', height=600, hovermode='closest',
                  legend_orientation='h')
    fig.update_xaxes(range=[history.Iteration.min(), history.Iteration.max()],
                     showgrid=True, gridwidth=1, gridcolor='Lightgray',
                     showline=True, linewidth=1, linecolor='Lightgray', mirror=True, row=1, col=1)
    fig.update_yaxes(range=[0, yLim], showgrid=True, gridwidth=1, gridcolor='Lightgray',
                     showline=True, linewidth=1, linecolor='Lightgray', mirror=True, row=1, col=1)
    # Right
    ind = np.linspace(0, history.shape[0], Table_Rows, endpoint = False).round(0).astype(int)
    ind = np.append(ind, history.Iteration.values[-1])
    history = history[history.index.isin(ind)]
    Temp = []
    for i in history.columns:
        Temp.append(history.loc[:,i].astype(float).round(4).values) 
    fig.add_trace(go.Table(header=dict(values = list(history.columns), line_color='darkslategray',
                                       fill_color='DimGray', align=['center','center'],
                                       font=dict(color='white', size=12), height=25), columnwidth = [0.4, 0.4, 0.4, 0.4],
                           cells=dict(values=Temp, line_color='darkslategray', fill=dict(color=['WhiteSmoke', 'white']),
                                      align=['center', 'center'], font_size=12,height=20)), 1, 2)
    if Title != False:
        fig.update_layout(plot_bgcolor= 'white',
                      title={'text': Title, 'x':0.46, 'y':0.94, 'xanchor': 'center', 'yanchor': 'top'},
                          yaxis_title='Frequency')
    fig.show()
    
def Confusion_Matrix(Model, X, y, Labels, FG = (14, 5)):
    fig, ax = plt.subplots(1, 2, figsize=FG)
    y_pred = Model.predict(X)
    if y.shape[1] > 1:
        CM = confusion_matrix(y.argmax(axis = 1), y_pred.argmax(axis = 1))
    else:
        CM = confusion_matrix(y, np.round(y_pred))

    _ = sns.heatmap(CM.round(2), annot=True, annot_kws={"size": 14}, cmap="Blues", ax = ax[0])
    _ = ax[0].set_xlabel('Predicted labels')
    _ = ax[0].set_ylabel('True labels'); 
    _ = ax[0].set_title('Confusion Matrix');
    _ = ax[0].xaxis.set_ticklabels(Labels)
    _ = ax[0].yaxis.set_ticklabels(Labels)

    CM = CM.astype('float') / CM.sum(axis=1)[:, np.newaxis]
    _ = sns.heatmap(CM.round(2), annot=True, annot_kws={"size": 14}, cmap="Greens", ax = ax[1],
                   linewidths = 0.2, vmin=0, vmax=1, cbar_kws={"shrink": 1})
    _ = ax[1].set_xlabel('Predicted labels')
    _ = ax[1].set_ylabel('True labels'); 
    _ = ax[1].set_title('Normalized Confusion Matrix');
    _ = ax[1].xaxis.set_ticklabels(Labels)
    _ = ax[1].yaxis.set_ticklabels(Labels)
    return fig, ax

Pred = model.predict(X_test)
def Pred_Plot(i, Pred = Pred, y_test = y_test, s = 1):
    Pred_Labels = np.argmax(Pred, axis = 1)
    True_Labels = np.argmax(y_test, axis =1)
    Img = X_test[i]*256
    Prop = Pred[i]
    fig, ax = plt.subplots(1, 2, figsize=(13*s, 6*s))
    # Left
    _ = ax[0].imshow(Img, cmap=plt.cm.binary)
    _ = ax[0].set_axis_off()

    if Pred_Labels[i] == True_Labels[i]:
        Color = 'blue'
    else:
        Color = 'red'
    _ = fig.suptitle('Predicted Label = %i, True Label = %i, Accuracy = %% %.2f' %
                     (Pred_Labels[i], True_Labels[i], 100*Pred.max(axis = 1)[i]), color=Color)
    # Right
    _ = sns.barplot(ax = ax[1], x= Labels, y= Prop, palette='summer', edgecolor='k',  hatch="///")
    _ = ax[1].set_yscale('log')
    _ = ax[1].set_ylim(top = 1)

Model Optimization Plot

In [8]:
Plot_history(Train_Table, yLim = 1, Title = 'Train Set')
Plot_history(Validation_Table, yLim = 1, Title = 'Validation Set')

Confusion Matrix

The confusion matrix allows for visualization of the performance of an algorithm.

In [9]:
fig, _ = Confusion_Matrix(model, X_train, y_train, Labels, FG = (17, 6))
_ = fig.suptitle('Train Set', fontsize = 16)
In [10]:
fig, _ = Confusion_Matrix(model, X_test, y_test, Labels, FG = (17, 6))
_ = fig.suptitle('Test Set', fontsize = 16)

Predictions

In [11]:
Pred_Labels = np.argmax(Pred, axis = 1)
True_Labels = np.argmax(y_test, axis =1)
# redicted Labels (Correct)
C = np.where(abs(Pred_Labels - True_Labels) == 0)[0]
# Predicted Labels (Wrong)
W = np.nonzero(abs(Pred_Labels - True_Labels))[0]

For example, we can randomly pick an entry from the correctly predicted labels list

In [12]:
Pred_Plot(np.random.choice(C), s = 3/4)

Similarly, from the wrongly predicted labels list

In [13]:
Pred_Plot(np.random.choice(W), s = 3/4)